/***********************************************************************************
Copy right:	    Coffee Tech.
Author:         wusong
Date:           2021年12月3日
Description:    和m0通信的相关函数
***********************************************************************************/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h> /*PPSIX 终端控制定义*/
#include <unistd.h>

#include "dc_pub.h"

#include "log_utils.h"
#include "m0_uart.h"
#include "nv_utils.h"

#define MODULE "m0_uart"

/****************************/
//用于接收串口的参数
int m0_fd;									 //代表m0的文件描述符
char uart_r_buf[sizeof(m0_env_info_t) * 2];	 //缓冲区
int total = sizeof(m0_env_info_t);
int header_total = sizeof(UART_MAGIC_HEADER) - 1;

extern short node_key_map[];
/***************************/

#define DEV_UART 			"/dev/ttyUSB0"

/**
 * @brief 打开并设置串口参数,串口号由nv_app.m0dev来指定
 * 
 */
void m0_init(void)
{
	struct termios options;

	char ttyname[32] = {0};

	int ttyfd = -1;
    ttyfd = open(DEV_UART, O_RDWR);

	if (ttyfd < 0)
	{
		perror("open tty failed");
		LOGE("can not open %s", ttyname);
		exit(-1);  //是否用一个assert
	}

	tcgetattr(ttyfd, &options);
	options.c_cflag |= (CLOCAL | CREAD);
	options.c_cflag &= ~CSIZE;
	options.c_cflag &= ~CRTSCTS;
	options.c_cflag |= CS8;
	options.c_cflag &= ~CSTOPB;
	options.c_iflag |= IGNPAR;
	options.c_iflag &= ~(ICRNL | IXON);
	options.c_oflag = 0;
	options.c_lflag = 0;
	cfsetispeed(&options, B115200);
	cfsetospeed(&options, B115200);
	tcsetattr(ttyfd, TCSANOW, &options);

	m0_fd = ttyfd;
}

inline void m0_uart_reopen()
{
	close(m0_fd);
	sleep(3);
	m0_init();
}

/**
 * @brief 阻塞式读取
 * 
 * @param fd 文件描述符
 * @param buf 缓冲区
 * @param len 长度
 * @return int 返回值,小于等于0代表失败
 */
int read_with_len_block(int fd, char *buf, int len)
{
	int left = len;
	char *p = buf;
	int ret = 0;
	while (left > 0)
	{
		ret = read(fd, p, left);

		if (ret <= 0)
		{
			break;
		}
		else
		{
			left -= ret;
			p += ret;
		}
	}

	return ret;
}

/**
 * @brief 开始接收数据前,先校验数据头
 * 
 * @param buf 缓冲区
 */
void m0_uart_get_header(char *buf)
{
	int ret = -1;
	int header_left = header_total;
	char *bufp = buf;
	while (1)
	{
		LOGD("header left %d\n", header_left);
		ret = read_with_len_block(m0_fd, bufp, header_left);
		if (ret <= 0)
		{
			LOGE("recv data from m0 error, it will be reopen soon");
			m0_uart_reopen();
			bufp = buf;
			header_left = header_total;
			continue;
		}

		bufp += ret;
		header_left -= ret;

		//检索头字节
		if (0 != memcmp(uart_r_buf, UART_MAGIC_HEADER, header_total))
		{
			memmove(uart_r_buf, uart_r_buf + 1, header_total - 1);
			header_left += 1;
			bufp -= 1;
			continue;
		}

		break;
	}
}

int m0_read_with_len_block(char *buf, int len)
{
	return read_with_len_block(m0_fd, buf, len);
}

/**
 * @brief 组命令,写给m0
 * 
 * @param dev 
 * @param on 
 * @return int 
 */
int m0_set(int dev, int on)
{
    LOGD("m0_set dev=%d, on=%d\n", dev, on);

    unsigned char buf = 0;

	switch (dev)
	{
	case DATA_TYPE_ID_ALARM:
		if (on)
		{
			buf = BEEP_ON;
		}
		else
		{
			buf = BEEP_OFF;
		}
		break;
	case DATA_TYPE_ID_LIGHT:
		if (on)
		{
			buf = LED_ON;
		}
		else
		{
			buf = LED_OFF;
		}
		break;
	case DATA_TYPE_ID_FAN:
		if (on)
		{
			buf = FAN_OFF | (0x0F & on);
		}
		else
		{
			buf = FAN_OFF;
		}
		break;

	default:
		break;
	}

    LOGN("write cmd buf = %x", buf);

	if (write(m0_fd, &buf, 1) < 0)
	{
		LOGE("write m0 fail %s", strerror(errno));
		return -1;
	}

	return 0;
}

int m0_set_cmd(int key, int val)
{
	int ret = 0;
	if (key == node_key_map[DATA_TYPE_ID_FAN])
	{
		//控制风扇
		ret = m0_set(DATA_TYPE_ID_FAN, val);
	}
	else if (key == node_key_map[DATA_TYPE_ID_ALARM])
	{
		//控制报警器
		ret = m0_set(DATA_TYPE_ID_ALARM, val);
	}
	else if (key == node_key_map[DATA_TYPE_ID_LIGHT])
	{
		//控制灯
		ret = m0_set(DATA_TYPE_ID_LIGHT, val);
	}
	else
	{
		LOGE("not match node key %d", key);
		sleep(1);
		return -1;
	}

	return ret;
}
